home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / fileops.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-03-14  |  66.2 KB  |  2,646 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
  3.  * Copyright (C) 1997 Josh MacDonald
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  */
  19.  
  20. #include "config.h"
  21.  
  22. #ifdef HAVE_SYS_PARAM_H
  23. #include <sys/param.h>
  24. #endif
  25.  
  26. #include <gtk/gtk.h>
  27.  
  28. #include <ctype.h>
  29. #include <errno.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <sys/types.h>
  33. #include <sys/stat.h>
  34. #ifdef HAVE_UNISTD_H
  35. #include <unistd.h>
  36. #endif
  37. #include <errno.h>
  38.  
  39. #ifdef G_OS_WIN32
  40. #include <direct.h>        /* For _mkdir() */
  41. #define mkdir(path,mode) _mkdir(path)
  42. #include <io.h>
  43. #ifndef S_IWUSR
  44. #define S_IWUSR _S_IWRITE
  45. #endif
  46. #ifndef S_IRUSR
  47. #define S_IRUSR _S_IREAD
  48. #endif
  49. #ifndef S_IWGRP
  50. #define S_IWGRP (_S_IWRITE>>3)
  51. #define S_IWOTH (_S_IWRITE>>6)
  52. #endif
  53. #ifndef S_IRGRP
  54. #define S_IRGRP (_S_IREAD>>3)
  55. #define S_IROTH (_S_IREAD>>6)
  56. #endif
  57. #define uid_t gint
  58. #define gid_t gint
  59. #define geteuid() 0
  60. #define getegid() 0
  61. #endif
  62.  
  63. #include "apptypes.h"
  64.  
  65. #include "appenv.h"
  66. #include "cursorutil.h"
  67. #include "dialog_handler.h"
  68. #include "gdisplay.h"
  69. #include "gimage.h"
  70. #include "gimpcontext.h"
  71. #include "gimpdrawableP.h"
  72. #include "gimpui.h"
  73. #include "fileops.h"
  74. #include "fileopsP.h"
  75. #include "menus.h"
  76. #include "layer.h"
  77. #include "channel.h"
  78. #include "plug_in.h"
  79. #include "procedural_db.h"
  80. #include "gimprc.h"
  81. #include "docindex.h"
  82. #include "undo.h"
  83.  
  84. #include "libgimp/gimpmath.h"
  85.  
  86. #include "libgimp/gimpintl.h"
  87.  
  88.  
  89. #define REVERT_DATA_KEY "revert_confirm_dialog"
  90.  
  91.  
  92. typedef struct _OverwriteData OverwriteData;
  93.  
  94. struct _OverwriteData
  95. {
  96.   gchar *full_filename;
  97.   gchar *raw_filename;
  98. };
  99.  
  100.  
  101. static void    file_overwrite               (GtkWidget     *parent,
  102.                          gchar         *filename,
  103.                          gchar         *raw_filename);
  104. static void    file_overwrite_callback      (GtkWidget     *widget,
  105.                          gboolean       overwrite,
  106.                          gpointer       data);
  107. static void    file_revert_confirm_callback (GtkWidget     *widget,
  108.                          gboolean       revert,
  109.                          gpointer       data);
  110.  
  111. static GimpImage * file_open_image          (gchar         *filename,
  112.                          gchar         *raw_filename,
  113.                                              PlugInProcDef *file_proc,
  114.                          gchar         *open_mode,
  115.                          RunModeType    run_mode,
  116.                          gint          *status);
  117.  
  118. static gint    file_open_with_proc          (gchar         *filename,
  119.                                              gchar         *raw_filename,
  120.                                              PlugInProcDef *file_proc);
  121.  
  122. static gint    file_save                    (GimpImage     *gimage,
  123.                          gchar         *filename,
  124.                          gchar         *raw_filename,
  125.                          RunModeType    run_mode);
  126.  
  127. static void    file_open_genbutton_callback (GtkWidget     *widget,
  128.                          gpointer       data);
  129.  
  130. static void    file_open_clistrow_callback  (GtkWidget     *widget,
  131.                          gint           row);
  132.  
  133. static void    file_open_ok_callback        (GtkWidget     *widget,
  134.                          gpointer       data);
  135.  
  136. static void    file_save_ok_callback        (GtkWidget     *widget,
  137.                          gpointer       data);
  138.  
  139. static void    file_dialog_show             (GtkWidget     *filesel);
  140. static gint    file_dialog_hide             (GtkWidget     *filesel);
  141. static void    file_update_name             (PlugInProcDef *proc,
  142.                          GtkWidget     *filesel);
  143.  
  144. static void    file_open_type_callback      (GtkWidget     *widget,
  145.                          gpointer       data);
  146. static void    file_save_type_callback      (GtkWidget     *widget,
  147.                          gpointer       data);
  148.  
  149. static void    file_convert_string          (gchar         *instr,
  150.                          gchar         *outmem,
  151.                          gint           maxmem,
  152.                          gint          *nmem);
  153.  
  154. static gchar * file_absolute_filename       (gchar         *name);
  155.  
  156. static gint    file_check_single_magic      (gchar         *offset,
  157.                          gchar         *type,
  158.                          gchar         *value,
  159.                          gint           headsize,
  160.                          guchar        *file_head,
  161.                          FILE          *ifp);
  162.  
  163. static gint    file_check_magic_list        (GSList        *magics_list,
  164.                          gint           headsize,
  165.                          guchar        *head,
  166.                          FILE          *ifp);
  167.  
  168. static void    file_update_menus            (GSList        *procs,
  169.                          gint           image_type);
  170.  
  171. static GSList* clist_to_slist               (GtkCList      *file_list);
  172.  
  173.  
  174.  
  175. static GtkWidget  *fileload     = NULL;
  176. static GtkWidget  *filesave     = NULL;
  177. static GtkWidget  *open_options = NULL;
  178. static GtkWidget  *save_options = NULL;
  179.  
  180. /* widgets for the open_options menu */
  181. static GtkPreview *open_options_preview        = NULL;
  182. static GtkWidget  *open_options_fixed          = NULL;
  183. static GtkWidget  *open_options_label          = NULL;
  184. static GtkWidget  *open_options_frame          = NULL;
  185. static GtkWidget  *open_options_genbuttonlabel = NULL;
  186.  
  187. /* Some state for the thumbnailer */
  188. static gchar *preview_fullname = NULL;
  189.  
  190. GSList *load_procs = NULL;
  191. GSList *save_procs = NULL;
  192.  
  193. static PlugInProcDef *load_file_proc = NULL;
  194. static PlugInProcDef *save_file_proc = NULL;
  195.  
  196. static GimpImage *the_gimage = NULL;
  197.  
  198. extern GSList *display_list; /* from gdisplay.c */
  199.  
  200.  
  201. #ifdef GDK_USE_UTF8_MBS
  202.  
  203. /* On Windows, this version of GIMP uses a branch of GTK+ 1.3.0 which
  204.  * uses UTF-8, except for two calls: gtk_file_selection_get_filename(),
  205.  * and gtk_file_selection_set_filename(), which use the current codepage.
  206.  *
  207.  * In retrospect, it might have been enough not to make GTK+ 1.3.0 use
  208.  * Unicode on Windows, but to stick to the current codepage, similar
  209.  * as GTK+ 1.2.x on Unix. But too late to change now. I didn't realize
  210.  * when doing the port that GTK+ 2.0 was going totally Unicode, and
  211.  * that I might have waited for that when I wanted Unicode support.
  212.  *
  213.  * This version of GIMP just barely works with non-ASCII filenames.
  214.  * The convention in this version of GIMP is that filenames stored and
  215.  * passed around are in the system codepage. When retrieved from or
  216.  * passed to GTK (other than the two APIs mentioned above) we have to
  217.  * convert back and forth. But there are still lots of places where
  218.  * the system codepage filenames are not converted even if they
  219.  * sprintfed together with internationalised messages that *are* in
  220.  * UTF-8. It's best to avoid non-ASCII filenames, sorry.
  221.  *
  222.  * Tor Lillqvist, 2002-12-16
  223.  */
  224.  
  225. /* Check that a string does not have UTF-8 encoded non-ASCII characters */
  226. static gboolean
  227. is_not_utf8 (const gchar *string)
  228. {
  229.   gunichar c;
  230.  
  231.   while (*string)
  232.     {
  233.       c = g_utf8_get_char (string);
  234.       if (c & 0x80000000)
  235.     return TRUE;
  236.       if (c > 0x7F)
  237.     return FALSE;
  238.       string = g_utf8_next_char (string);
  239.     }
  240.   return TRUE;
  241. }
  242.  
  243. #define ASSERT_NOT_UTF8(filename) g_assert (is_not_utf8 (filename))
  244. #define ASSERT_UTF8(filename) g_assert (g_utf8_validate (filename, -1, NULL))
  245.  
  246. #endif
  247.  
  248. void
  249. file_ops_pre_init (void)
  250. {
  251. }
  252.  
  253. void
  254. file_ops_post_init (void)
  255. {
  256.   GimpItemFactoryEntry entry;
  257.   PlugInProcDef *file_proc;
  258.   GSList *tmp;
  259.  
  260.   load_procs = g_slist_reverse (load_procs);
  261.   save_procs = g_slist_reverse (save_procs);
  262.  
  263.   for (tmp = load_procs; tmp;  tmp = g_slist_next (tmp))
  264.     {
  265.       gchar *help_page;
  266.  
  267.       file_proc = tmp->data;
  268.  
  269.       help_page = g_strconcat ("filters" G_DIR_SEPARATOR_S,
  270.                    g_basename (file_proc->prog),
  271.                    ".html",
  272.                    NULL);
  273.       g_strdown (help_page);
  274.  
  275.       entry.entry.path            = file_proc->menu_path;
  276.       entry.entry.accelerator     = NULL;
  277.       entry.entry.callback        = file_open_type_callback;
  278.       entry.entry.callback_action = 0;
  279.       entry.entry.item_type       = NULL;
  280.       entry.help_page             = help_page;
  281.       entry.description           = NULL;
  282.  
  283.       menus_create_item_from_full_path (&entry, NULL, file_proc);
  284.     }
  285.  
  286.   for (tmp = save_procs; tmp; tmp = g_slist_next (tmp))
  287.     {
  288.       gchar *help_page;
  289.  
  290.       file_proc = tmp->data;
  291.  
  292.       help_page = g_strconcat ("filters" G_DIR_SEPARATOR_S,
  293.                    g_basename (file_proc->prog),
  294.                    ".html",
  295.                    NULL);
  296.       g_strdown (help_page);
  297.  
  298.       entry.entry.path            = file_proc->menu_path;
  299.       entry.entry.accelerator     = NULL;
  300.       entry.entry.callback        = file_save_type_callback;
  301.       entry.entry.callback_action = 0;
  302.       entry.entry.item_type       = NULL;
  303.       entry.help_page             = help_page;
  304.       entry.description           = NULL;
  305.  
  306.       menus_create_item_from_full_path (&entry, NULL, file_proc);
  307.     }
  308. }
  309.  
  310. void
  311. file_open_callback (GtkWidget *widget,
  312.             gpointer   data)
  313. {
  314.   if (!fileload)
  315.     {
  316.       fileload = gtk_file_selection_new (_("Load Image"));
  317.       gtk_window_set_position (GTK_WINDOW (fileload), GTK_WIN_POS_MOUSE);
  318.       gtk_window_set_wmclass (GTK_WINDOW (fileload), "load_image", "Gimp");
  319.  
  320.       gtk_container_set_border_width (GTK_CONTAINER (fileload), 2);
  321.       gtk_container_set_border_width 
  322.     (GTK_CONTAINER (GTK_FILE_SELECTION (fileload)->button_area), 2);
  323.  
  324.       dialog_register_fileload (fileload);
  325.  
  326.       gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->cancel_button),
  327.                  "clicked",
  328.                  GTK_SIGNAL_FUNC (file_dialog_hide),
  329.                  GTK_OBJECT (fileload));
  330.       gtk_signal_connect (GTK_OBJECT (fileload), "delete_event",
  331.               GTK_SIGNAL_FUNC (file_dialog_hide),
  332.               NULL);
  333.       gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->ok_button),
  334.               "clicked",
  335.               GTK_SIGNAL_FUNC (file_open_ok_callback),
  336.               fileload);
  337.  
  338.       gtk_clist_set_selection_mode (GTK_CLIST
  339.                     (GTK_FILE_SELECTION (fileload)->file_list),
  340.                     GTK_SELECTION_EXTENDED);
  341.  
  342.       /* Catch file-clist clicks so we can update the preview thumbnail */
  343.       gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->file_list),
  344.               "select_row",
  345.               GTK_SIGNAL_FUNC (file_open_clistrow_callback),
  346.               fileload);
  347.  
  348.       /*  Connect the "F1" help key  */
  349.       gimp_help_connect_help_accel (fileload,
  350.                     gimp_standard_help_func,
  351.                     "open/dialogs/file_open.html");
  352.     }
  353.   else
  354.     {
  355.       gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE);
  356.       if (GTK_WIDGET_VISIBLE (fileload))
  357.     return;
  358.  
  359.       gtk_file_selection_set_filename (GTK_FILE_SELECTION (fileload),
  360.                        "." G_DIR_SEPARATOR_S);
  361.       gtk_window_set_title (GTK_WINDOW (fileload), _("Load Image"));
  362.     }
  363.  
  364.   if (!open_options)
  365.     {
  366.       GtkWidget *frame;
  367.       GtkWidget *vbox;
  368.       GtkWidget *hbox;
  369.       GtkWidget *option_menu;
  370.       GtkWidget *load_menu;
  371.       GtkWidget *open_options_genbutton;
  372.  
  373.       open_options = gtk_hbox_new (TRUE, 1);
  374.  
  375.       /* format-chooser frame */
  376.       frame = gtk_frame_new (_("Determine File Type"));
  377.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  378.       gtk_box_pack_start (GTK_BOX (open_options), frame, TRUE, TRUE, 4);
  379.  
  380.       vbox = gtk_vbox_new (FALSE, 2);
  381.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  382.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  383.  
  384.       hbox = gtk_hbox_new (FALSE, 0);
  385.       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  386.       gtk_widget_show (hbox);
  387.  
  388.       option_menu = gtk_option_menu_new ();
  389.       gtk_box_pack_start (GTK_BOX (hbox), option_menu, FALSE, FALSE, 0);
  390.       gtk_widget_show (option_menu);
  391.  
  392.       menus_get_load_menu (&load_menu, NULL);
  393.       gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), load_menu);
  394.  
  395.       gtk_widget_show (vbox);
  396.       gtk_widget_show (frame);
  397.  
  398.       /* Preview frame */
  399.       open_options_frame = frame = gtk_frame_new ("");
  400.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  401.       gtk_box_pack_end (GTK_BOX (open_options), frame, FALSE, TRUE, 4);
  402.  
  403.       vbox = gtk_vbox_new (FALSE, 2);
  404.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  405.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  406.  
  407.       hbox = gtk_hbox_new (TRUE, 0);
  408.       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  409.       gtk_widget_show (hbox);
  410.  
  411.       open_options_genbutton = gtk_button_new ();
  412.       gtk_signal_connect (GTK_OBJECT (open_options_genbutton), "clicked",
  413.               GTK_SIGNAL_FUNC (file_open_genbutton_callback),
  414.               fileload);
  415.       gtk_box_pack_start (GTK_BOX (hbox), open_options_genbutton,
  416.               TRUE, FALSE, 0);
  417.       gtk_widget_show (open_options_genbutton);    
  418.  
  419.       open_options_fixed = gtk_fixed_new ();
  420.       gtk_widget_set_usize (open_options_fixed, 80, 60);
  421.       gtk_container_add (GTK_CONTAINER (GTK_BIN (open_options_genbutton)),
  422.              open_options_fixed);
  423.       gtk_widget_show (open_options_fixed);
  424.  
  425.       {
  426.     GtkWidget* abox;
  427.     GtkWidget* sbox;
  428.     GtkWidget* align;
  429.  
  430.     sbox = gtk_vbox_new (TRUE, 0);
  431.     gtk_container_add (GTK_CONTAINER (open_options_fixed), sbox);
  432.     gtk_widget_show (sbox);
  433.  
  434.     align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  435.     gtk_widget_set_usize (align, 80, 60);
  436.     gtk_box_pack_start (GTK_BOX (sbox), align, FALSE, TRUE, 0);
  437.     gtk_widget_show (align);
  438.  
  439.     abox = gtk_hbox_new (FALSE, 0);
  440.     gtk_container_add (GTK_CONTAINER (align), abox);
  441.     gtk_widget_show (abox);
  442.  
  443.     open_options_preview =
  444.       GTK_PREVIEW (gtk_preview_new (GTK_PREVIEW_COLOR));
  445.     gtk_box_pack_start (GTK_BOX (abox), GTK_WIDGET (open_options_preview),
  446.                 FALSE, TRUE, 0);
  447.     gtk_widget_show (GTK_WIDGET (open_options_preview));
  448.  
  449.     open_options_genbuttonlabel = gtk_label_new (_("Generate\nPreview"));
  450.     gtk_box_pack_start (GTK_BOX (abox), open_options_genbuttonlabel,
  451.                 FALSE, TRUE, 0);
  452.     gtk_widget_show (open_options_genbuttonlabel);
  453.       }
  454.  
  455.       open_options_label = gtk_label_new ("");
  456.       gtk_box_pack_start (GTK_BOX (vbox), open_options_label, FALSE, FALSE, 0);
  457.       gtk_widget_show (open_options_label); 
  458.  
  459.       gtk_widget_show (vbox);
  460.       gtk_widget_show (frame);
  461.  
  462.       /* pack the containing open_options hbox into the open-dialog */
  463.       gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (fileload)->main_vbox),
  464.             open_options, FALSE, FALSE, 0);
  465.     }
  466.  
  467.   gtk_frame_set_label (GTK_FRAME (open_options_frame), _("Preview"));
  468.   gtk_label_set_text (GTK_LABEL (open_options_label), _("No Selection."));
  469.  
  470.   gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel));
  471.   gtk_widget_hide (GTK_WIDGET (open_options_preview));
  472.   gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), FALSE);
  473.  
  474.   gtk_widget_show (open_options);
  475.  
  476.   file_dialog_show (fileload);
  477. }
  478.  
  479. void
  480. file_save_callback (GtkWidget *widget,
  481.             gpointer   data)
  482. {
  483.   GDisplay *gdisplay;
  484.  
  485.   gdisplay = gdisplay_active ();
  486.   if (! gdisplay)
  487.     return;
  488.  
  489.   if (! gimage_active_drawable (gdisplay->gimage))
  490.     return;
  491.  
  492.   /*  Only save if the gimage has been modified  */
  493.   if (!trust_dirty_flag || gdisplay->gimage->dirty != 0)
  494.     {
  495.       if (gdisplay->gimage->has_filename == FALSE)
  496.     {
  497.       file_save_as_callback (widget, data);
  498.     }
  499.       else
  500.     {
  501.       gchar *filename;
  502.       gchar *raw_filename;
  503.       gint   status;
  504.  
  505.       filename     = g_strdup (gimage_filename (gdisplay->gimage));
  506.       
  507. #ifdef GDK_USE_UTF8_MBS
  508.       ASSERT_UTF8 (filename);
  509.       {
  510.         gchar *tmp = filename;
  511.         filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
  512.         g_free (tmp);
  513.       }
  514. #endif
  515.       raw_filename = g_basename (filename);
  516.  
  517.       status = file_save (gdisplay->gimage,
  518.                   filename,
  519.                   raw_filename,
  520.                   RUN_WITH_LAST_VALS);
  521.  
  522.       if (status != PDB_SUCCESS &&
  523.           status != PDB_CANCEL)
  524.         {
  525. #ifdef GDK_USE_UTF8_MBS
  526.           gchar *utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
  527.           g_message (_("Save failed.\n%s"), utf8_filename);
  528.           g_free (utf8_filename);
  529. #else
  530.           g_message (_("Save failed.\n%s"), filename);
  531. #endif
  532.         }
  533.  
  534.       g_free (filename);
  535.     }
  536.     }
  537. }
  538.  
  539. void
  540. file_save_as_callback (GtkWidget *widget,
  541.                gpointer   data)
  542. {
  543.   GDisplay *gdisplay;
  544.  
  545.   gdisplay = gdisplay_active ();
  546.   if (! gdisplay)
  547.     return;
  548.  
  549.   if (! gimage_active_drawable (gdisplay->gimage))
  550.     return;
  551.  
  552.   the_gimage = gdisplay->gimage;
  553.  
  554.   if (!filesave)
  555.     {
  556.       filesave = gtk_file_selection_new (_("Save Image"));
  557.       gtk_window_set_position (GTK_WINDOW (filesave), GTK_WIN_POS_MOUSE);
  558.       gtk_window_set_wmclass (GTK_WINDOW (filesave), "save_image", "Gimp");
  559.  
  560.       gtk_container_set_border_width (GTK_CONTAINER (filesave), 2);
  561.       gtk_container_set_border_width 
  562.     (GTK_CONTAINER (GTK_FILE_SELECTION (filesave)->button_area), 2);
  563.  
  564.       gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->cancel_button),
  565.                  "clicked",
  566.                  GTK_SIGNAL_FUNC (file_dialog_hide),
  567.                  GTK_OBJECT (filesave));
  568.       gtk_signal_connect (GTK_OBJECT (filesave), "delete_event",
  569.               GTK_SIGNAL_FUNC (file_dialog_hide),
  570.               NULL);
  571.       gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->ok_button),
  572.               "clicked",
  573.               GTK_SIGNAL_FUNC (file_save_ok_callback),
  574.               filesave);
  575.  
  576.       /*  Connect the "F1" help key  */
  577.       gimp_help_connect_help_accel (filesave,
  578.                     gimp_standard_help_func,
  579.                     "save/dialogs/file_save.html");
  580.     }
  581.   else
  582.     {
  583.       gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE);
  584.       if (GTK_WIDGET_VISIBLE (filesave))
  585.     return;
  586.  
  587.       gtk_window_set_title (GTK_WINDOW (filesave), _("Save Image"));
  588.     }
  589.  
  590. #ifdef GDK_USE_UTF8_MBS
  591.   {
  592.     gchar *tmp = g_filename_from_utf8 (gdisplay->gimage->has_filename
  593.                        ? gimage_filename(gdisplay->gimage)
  594.                        : "." G_DIR_SEPARATOR_S,
  595.                        -1, NULL, NULL, NULL);
  596.     gtk_file_selection_set_filename (GTK_FILE_SELECTION(filesave), tmp);
  597.  
  598.     g_free (tmp);
  599.   }
  600. #else
  601.   gtk_file_selection_set_filename (GTK_FILE_SELECTION(filesave),
  602.                                    gdisplay->gimage->has_filename
  603.                                    ? gimage_filename(gdisplay->gimage)
  604.                                    : "." G_DIR_SEPARATOR_S);
  605. #endif
  606.  
  607.   if (!save_options)
  608.     {
  609.       GtkWidget *frame;
  610.       GtkWidget *hbox;
  611.       GtkWidget *label;
  612.       GtkWidget *option_menu;
  613.       GtkWidget *save_menu;
  614.  
  615.       save_options = gtk_hbox_new (TRUE, 1);
  616.  
  617.       frame = gtk_frame_new (_("Save Options"));
  618.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  619.       gtk_box_pack_start (GTK_BOX (save_options), frame, TRUE, TRUE, 4);
  620.  
  621.       hbox = gtk_hbox_new (FALSE, 4);
  622.       gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
  623.       gtk_container_add (GTK_CONTAINER (frame), hbox);
  624.       gtk_widget_show (hbox);
  625.  
  626.       label = gtk_label_new (_("Determine File Type:"));
  627.       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  628.       gtk_widget_show (label);
  629.  
  630.       option_menu = gtk_option_menu_new ();
  631.       gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 0);
  632.       gtk_widget_show (option_menu);
  633.  
  634.       menus_get_save_menu (&save_menu, NULL);
  635.       gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), save_menu);
  636.  
  637.       gtk_widget_show (frame);
  638.  
  639.       /* pack the containing save_options hbox into the save-dialog */
  640.       gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (filesave)->main_vbox),
  641.             save_options, FALSE, FALSE, 0);
  642.     }
  643.  
  644.   switch (drawable_type (gimage_active_drawable (gdisplay->gimage)))
  645.     {
  646.     case RGB_GIMAGE:
  647.       file_update_menus (save_procs, PLUG_IN_RGB_IMAGE);
  648.       break;
  649.     case RGBA_GIMAGE:
  650.       file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE);
  651.       break;
  652.     case GRAY_GIMAGE:
  653.       file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE);
  654.       break;
  655.     case GRAYA_GIMAGE:
  656.       file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE);
  657.       break;
  658.     case INDEXED_GIMAGE:
  659.       file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE);
  660.       break;
  661.     case INDEXEDA_GIMAGE:
  662.       file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE);
  663.       break;
  664.     }
  665.  
  666.   gtk_widget_show (save_options);
  667.  
  668.   file_dialog_show (filesave);
  669. }
  670.  
  671. void
  672. file_revert_callback (GtkWidget *widget,
  673.               gpointer   data)
  674. {
  675.   GDisplay  *gdisplay;
  676.   GimpImage *gimage;
  677.   GtkWidget *query_box;
  678.  
  679.   gdisplay = gdisplay_active ();
  680.   if (!gdisplay || !gdisplay->gimage)
  681.     return;
  682.  
  683.   gimage = gdisplay->gimage;
  684.  
  685.   query_box = gtk_object_get_data (GTK_OBJECT (gimage), REVERT_DATA_KEY);
  686.  
  687.   if (gimage->has_filename == FALSE)
  688.     {
  689.       g_message (_("Revert failed.\n"
  690.            "No filename associated with this image."));
  691.     }
  692.   else if (query_box)
  693.     {
  694.       gdk_window_raise (query_box->window);
  695.     }
  696.   else
  697.     {
  698.       gchar *text;
  699.  
  700.       text = g_strdup_printf (_("Reverting %s to\n"
  701.                 "%s\n\n"
  702.                 "(You will lose all your changes\n"
  703.                 "including all undo information)"),
  704.                   g_basename (gimage_filename (gimage)),
  705.                   gimage_filename (gimage));
  706.  
  707.       query_box = gimp_query_boolean_box (_("Revert Image?"),
  708.                       gimp_standard_help_func,
  709.                       "file/revert.html",
  710.                       FALSE,
  711.                       text,
  712.                       _("Yes"), _("No"),
  713.                       GTK_OBJECT (gimage), "destroy",
  714.                       file_revert_confirm_callback,
  715.                       gimage);
  716.  
  717.       g_free (text);
  718.  
  719.       gtk_object_set_data (GTK_OBJECT (gimage), REVERT_DATA_KEY, query_box);
  720.  
  721.       gtk_widget_show (query_box);
  722.     }
  723. }
  724.  
  725. void
  726. file_open_by_extension_callback (GtkWidget *widget,
  727.                  gpointer   data)
  728. {
  729.   load_file_proc = NULL;
  730. }
  731.  
  732. void
  733. file_save_by_extension_callback (GtkWidget *widget,
  734.                  gpointer   data)
  735. {
  736.   save_file_proc = NULL;
  737. }
  738.  
  739. static void
  740. file_update_name (PlugInProcDef *proc,
  741.           GtkWidget     *filesel)
  742. {
  743.   if (proc->extensions_list)
  744.     {
  745.       gchar *text;
  746.       gchar *last_dot;
  747.       GString *s;
  748.  
  749.       text = gtk_entry_get_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry));
  750.       last_dot = strrchr (text, '.');
  751.  
  752.       if (last_dot == text || !text[0])
  753.     return;
  754.  
  755.       s = g_string_new (text);
  756.  
  757.       if (last_dot)
  758.     g_string_truncate (s, last_dot-text);
  759.  
  760.       g_string_append (s, ".");
  761.       g_string_append (s, (gchar *) proc->extensions_list->data);
  762.  
  763.       gtk_entry_set_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry), s->str);
  764.  
  765.       g_string_free (s, TRUE);
  766.     }
  767. }
  768.  
  769. static void
  770. file_open_type_callback (GtkWidget *widget,
  771.              gpointer   data)
  772. {
  773.   PlugInProcDef *proc = (PlugInProcDef *) data;
  774.  
  775.   file_update_name (proc, fileload);
  776.  
  777.   load_file_proc = proc;
  778. }
  779.  
  780. static void
  781. file_save_type_callback (GtkWidget *widget,
  782.              gpointer   data)
  783. {
  784.   PlugInProcDef *proc = (PlugInProcDef *) data;
  785.  
  786.   file_update_name (proc, filesave);
  787.  
  788.   save_file_proc = proc;
  789. }
  790.  
  791. static GimpImage *
  792. file_open_image (gchar          *filename,
  793.          gchar          *raw_filename,
  794.                  PlugInProcDef  *file_proc,
  795.          gchar          *open_mode,
  796.          RunModeType     run_mode, 
  797.          gint           *status)
  798. {
  799.   ProcRecord *proc;
  800.   Argument   *args;
  801.   Argument   *return_vals;
  802.   gint gimage_id;
  803.   gint i;
  804.   struct stat statbuf;
  805.  
  806. #ifdef GDK_USE_UTF8_MBS
  807.   ASSERT_NOT_UTF8 (filename);
  808.   ASSERT_NOT_UTF8 (raw_filename);
  809. #endif
  810.  
  811. #ifdef G_PLATFORM_WIN32
  812.   {
  813.     guchar *p = filename;
  814.     gboolean non_ascii = FALSE;
  815.     static gboolean warned = FALSE;
  816.  
  817.     while (*p)
  818.       if (*p++ > 0x7F)
  819.     non_ascii = TRUE;
  820.     
  821.     if (non_ascii && !warned)
  822.       {
  823.     g_message ("This version of GIMP does not fully support non-ASCII\n"
  824.            "filenames. This will be fixed in GIMP 1.4.");
  825.     warned = TRUE;
  826.       }
  827.   }
  828. #endif
  829.  
  830.   *status = PDB_CANCEL;  /* inhibits error messages by caller */
  831.  
  832.   if (!file_proc)
  833.     file_proc = file_proc_find (load_procs, filename);
  834.  
  835.   if (!file_proc)
  836.     {
  837.       /*  no errors when making thumbnails  */
  838.       if (run_mode == RUN_INTERACTIVE)
  839.     {
  840. #ifdef GDK_USE_UTF8_MBS
  841.       gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  842.                              NULL, NULL, NULL);
  843.       g_message (_("%s failed.\n"
  844.                "%s: Unknown file type."),
  845.              open_mode, utf8_filename);
  846.       g_free (utf8_filename);
  847. #else
  848.       g_message (_("%s failed.\n"
  849.                "%s: Unknown file type."),
  850.              open_mode, filename);
  851. #endif
  852.     }
  853.  
  854.       return NULL;
  855.     }
  856.  
  857.   /* check if we are opening a file */
  858.   if (stat (filename, &statbuf) == 0)
  859.     {
  860.       uid_t euid;
  861.       gid_t egid;
  862.  
  863.       if (! (statbuf.st_mode & S_IFREG))
  864.     {
  865.       /*  no errors when making thumbnails  */
  866.       if (run_mode == RUN_INTERACTIVE)
  867.         {
  868. #ifdef GDK_USE_UTF8_MBS
  869.           gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  870.                              NULL, NULL, NULL);
  871.           g_message (_("%s failed.\n"
  872.                "%s is not a regular file."),
  873.              open_mode, utf8_filename);
  874.           g_free (utf8_filename);
  875. #else
  876.           g_message (_("%s failed.\n"
  877.                "%s is not a regular file."),
  878.              open_mode, filename);
  879. #endif
  880.         }
  881.  
  882.       return NULL;
  883.     }
  884.  
  885.       euid = geteuid ();
  886.       egid = getegid ();
  887.  
  888.       if (! ((statbuf.st_mode & S_IRUSR) ||
  889.  
  890.          ((statbuf.st_mode & S_IRGRP) &&
  891.           (statbuf.st_uid != euid)) ||
  892.  
  893.          ((statbuf.st_mode & S_IROTH) &&
  894.           (statbuf.st_uid != euid) &&
  895.           (statbuf.st_gid != egid))))
  896.     {
  897.       /*  no errors when making thumbnails  */
  898.       if (run_mode == RUN_INTERACTIVE)
  899.         {
  900. #ifdef GDK_USE_UTF8_MBS
  901.           gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  902.                              NULL, NULL, NULL);
  903.           g_message (_("%s failed.\n"
  904.                "%s: Permission denied."),
  905.              open_mode, utf8_filename);
  906.           g_free (utf8_filename);
  907. #else
  908.           g_message (_("%s failed.\n"
  909.                "%s: Permission denied."),
  910.              open_mode, filename);
  911. #endif
  912.         }
  913.  
  914.       return NULL;
  915.     }
  916.     }
  917.  
  918.   proc = &file_proc->db_info;
  919.  
  920.   args = g_new0 (Argument, proc->num_args);
  921.  
  922.   for (i = 0; i < proc->num_args; i++)
  923.     args[i].arg_type = proc->args[i].arg_type;
  924.  
  925.   args[0].value.pdb_int     = run_mode;
  926.   args[1].value.pdb_pointer = filename;
  927.   args[2].value.pdb_pointer = raw_filename;
  928.  
  929.   return_vals = procedural_db_execute (proc->name, args);
  930.  
  931.   *status   = return_vals[0].value.pdb_int;
  932.   gimage_id = return_vals[1].value.pdb_int;
  933.  
  934.   procedural_db_destroy_args (return_vals, proc->num_values);
  935.   g_free (args);
  936.  
  937.   if (*status == PDB_SUCCESS && gimage_id != -1)
  938.     {
  939.       GimpImage *gimage = gimage_get_ID (gimage_id);
  940.  
  941.       if (gimage)
  942.     {
  943.       layer_invalidate_previews (gimage);
  944.       channel_invalidate_previews (gimage);
  945.     }
  946.  
  947.       return pdb_id_to_image (gimage_id);
  948.     }
  949.  
  950.   return NULL;
  951. }
  952.  
  953. static gint
  954. file_open_with_proc (gchar         *filename,
  955.                      gchar         *raw_filename,
  956.                      PlugInProcDef *file_proc)
  957. {
  958.   GimpImage *gimage;
  959.   GDisplay  *gdisplay;
  960.   gchar     *absolute;
  961.   gint       status;
  962.  
  963. #ifdef GDK_USE_UTF8_MBS
  964.   ASSERT_NOT_UTF8 (filename);
  965.   ASSERT_NOT_UTF8 (raw_filename);
  966. #endif
  967.  
  968.   if ((gimage = file_open_image (filename,
  969.                  raw_filename,
  970.                                  file_proc,
  971.                  _("Open"),
  972.                  RUN_INTERACTIVE,
  973.                  &status)) != NULL)
  974.     {
  975.       /* enable & clear all undo steps */
  976.       gimage_enable_undo (gimage);
  977.  
  978.       /* set the image to clean  */
  979.       gimage_clean_all (gimage);
  980.  
  981.       /* display the image */
  982.       gdisplay = gdisplay_new (gimage, 0x0101);
  983.  
  984.       /* always activate the first display */
  985.       if (g_slist_length (display_list) == 1)
  986.     gimp_context_set_display (gimp_context_get_user (), gdisplay);
  987.  
  988.       absolute = file_absolute_filename (filename);
  989.       document_index_add (absolute);
  990.       if (!no_interface)
  991.         menus_last_opened_add (absolute);
  992.       g_free (absolute);
  993.     }
  994.  
  995.   return status;
  996. }
  997.  
  998. gint
  999. file_open (gchar *filename,
  1000.            gchar *raw_filename)
  1001. {
  1002.   return file_open_with_proc (filename, raw_filename, NULL);
  1003. }
  1004.  
  1005. TempBuf *
  1006. make_thumb_tempbuf (GimpImage *gimage)
  1007. {
  1008.   gint w, h;
  1009.  
  1010.   if (gimage->width<=80 && gimage->height<=60)
  1011.     {
  1012.       w = gimage->width;
  1013.       h = gimage->height;
  1014.     }
  1015.   else
  1016.     {
  1017.       /* Ratio molesting to fit within .xvpic thumbnail size limits */
  1018.       if (60 * gimage->width < 80 * gimage->height)
  1019.     {
  1020.       h = 60;
  1021.       w = (60 * gimage->width) / gimage->height;
  1022.       if (w == 0)
  1023.         w = 1;
  1024.     }
  1025.       else
  1026.     {
  1027.       w = 80;
  1028.       h = (80 * gimage->height) / gimage->width;
  1029.       if (h == 0)
  1030.         h = 1;
  1031.     }
  1032.     }
  1033.  
  1034.   /*printf("tn: %d x %d -> ", w, h);fflush(stdout);*/
  1035.  
  1036.   return (gimp_image_composite_preview (gimage, GRAY_CHANNEL, w, h));
  1037. }
  1038.  
  1039. static guchar *
  1040. make_RGBbuf_from_tempbuf (TempBuf *tempbuf,
  1041.               gint    *width_rtn,
  1042.               gint    *height_rtn)
  1043. {
  1044.   gint    i, j, w, h;
  1045.   guchar *tbd;
  1046.   guchar *ptr;
  1047.   guchar *rtn = NULL;
  1048.   guchar  alpha, r, g, b;
  1049.  
  1050.   w = (*width_rtn) = tempbuf->width;
  1051.   h = (*height_rtn) = tempbuf->height;
  1052.   tbd = temp_buf_data (tempbuf);
  1053.  
  1054.   switch (tempbuf->bytes)
  1055.     {
  1056.     case 4:
  1057.       rtn = ptr = g_malloc (3 * w * h);
  1058.       for (i=0; i<h; i++)
  1059.     {
  1060.       for (j=0; j<w; j++)
  1061.         {
  1062.           r = *(tbd++);
  1063.           g = *(tbd++);
  1064.           b = *(tbd++);
  1065.           alpha = *(tbd++);
  1066.  
  1067.           if (alpha & 128)
  1068.         {
  1069.           *(ptr++) = r;
  1070.           *(ptr++) = g;
  1071.           *(ptr++) = b;
  1072.         }
  1073.           else
  1074.         {
  1075.           r = (( (i^j) & 4 ) << 5) | 64;
  1076.           *(ptr++) = r;
  1077.           *(ptr++) = r;
  1078.           *(ptr++) = r;
  1079.         }
  1080.         }
  1081.     }
  1082.       break;
  1083.  
  1084.     case 2:
  1085.       rtn = ptr = g_malloc (3 * w * h);
  1086.       for (i=0; i<h; i++)
  1087.     {
  1088.       for (j=0; j<w; j++)
  1089.         {
  1090.           r = *(tbd++);
  1091.           alpha = *(tbd++);
  1092.  
  1093.           if (!(alpha & 128))
  1094.         r = (( (i^j) & 4 ) << 5) | 64;
  1095.  
  1096.           *(ptr++) = r;
  1097.           *(ptr++) = r;
  1098.           *(ptr++) = r;
  1099.         }
  1100.     }
  1101.       break;
  1102.  
  1103.     default:
  1104.       g_warning("UNKNOWN TempBuf width in make_RGBbuf_from_tempbuf()");
  1105.     }
  1106.  
  1107.   return (rtn);
  1108. }
  1109.  
  1110. gboolean
  1111. file_save_thumbnail (GimpImage  *gimage,
  1112.              const char *full_source_filename,
  1113.              TempBuf    *tempbuf)
  1114. {
  1115.   gint i,j;
  1116.   gint w,h;
  1117.   guchar *tbd;
  1118.   gchar* pathname;
  1119.   gchar* filename;
  1120.   gchar* xvpathname;
  1121.   gchar* thumbnailname;
  1122.   GimpImageBaseType basetype;
  1123.   FILE *fp;
  1124.   struct stat statbuf;
  1125.  
  1126. #ifdef GDK_USE_UTF8_MBS
  1127.   ASSERT_NOT_UTF8 (full_source_filename);
  1128. #endif
  1129.  
  1130.   if (stat (full_source_filename, &statbuf) != 0)
  1131.     {
  1132.       return FALSE;
  1133.     }
  1134.  
  1135.   /* just for debugging 
  1136.    *  if (gimp_image_preview_valid (gimage, GRAY_CHANNEL))
  1137.    *   {
  1138.    *     g_print ("(incidentally, gimage already has a valid preview - %dx%d)\n",
  1139.    *             gimage->comp_preview->width,
  1140.    *             gimage->comp_preview->height);
  1141.    *   }
  1142.    */
  1143.  
  1144.   pathname = g_dirname (full_source_filename);
  1145.   filename = g_basename (full_source_filename); /* Don't free! */
  1146.  
  1147.   xvpathname = g_strconcat (pathname, G_DIR_SEPARATOR_S, ".xvpics",
  1148.                 NULL);
  1149.  
  1150.   thumbnailname = g_strconcat (xvpathname, G_DIR_SEPARATOR_S,
  1151.                    filename,
  1152.                    NULL);
  1153.  
  1154.   tbd = temp_buf_data (tempbuf);
  1155.  
  1156.   w = tempbuf->width;
  1157.   h = tempbuf->height;
  1158.   /*printf("tn: %d x %d\n", w, h);fflush(stdout);*/
  1159.  
  1160.   mkdir (xvpathname, 0755);
  1161.  
  1162.   fp = fopen (thumbnailname, "wb");
  1163.   g_free (pathname);
  1164.   g_free (xvpathname);
  1165.   g_free (thumbnailname);
  1166.  
  1167.   if (fp)
  1168.     {
  1169.       basetype = gimp_image_base_type(gimage);
  1170.  
  1171.       fprintf (fp,
  1172.            "P7 332\n#IMGINFO:%dx%d %s (%d %s)\n"
  1173.            "#END_OF_COMMENTS\n%d %d 255\n",
  1174.            gimage->width, gimage->height,
  1175.            (basetype == RGB) ? "RGB" :
  1176.            (basetype == GRAY) ? "Greyscale" :
  1177.            (basetype == INDEXED) ? "Indexed" :
  1178.            "(UNKNOWN COLOUR TYPE)",
  1179.            (int)statbuf.st_size,
  1180.            (statbuf.st_size == 1) ? "byte" : "bytes",
  1181.            w, h);
  1182.  
  1183.       switch (basetype)
  1184.     {
  1185.     case INDEXED:
  1186.     case RGB:
  1187.       for (i=0; i<h; i++)
  1188.         {
  1189.           /* Do a cheap unidirectional error-spread to look better */
  1190.           gint rerr=0, gerr=0, berr=0, a;
  1191.  
  1192.           for (j=0; j<w; j++)
  1193.         {
  1194.           gint32 r,g,b;
  1195.  
  1196.           if (128 & *(tbd + 3))
  1197.             {
  1198.               r = *(tbd++) + rerr;
  1199.               g = *(tbd++) + gerr;
  1200.               b = *(tbd++) + berr;
  1201.               tbd++;
  1202.             }
  1203.           else
  1204.             {
  1205.               a = (( (i^j) & 4 ) << 5) | 64; /* cute. */
  1206.               r = a + rerr;
  1207.               g = a + gerr;
  1208.               b = a + berr;
  1209.               tbd += 4;
  1210.             }
  1211.  
  1212.           r = CLAMP0255 (r);
  1213.           g = CLAMP0255 (g);
  1214.           b = CLAMP0255 (b);
  1215.  
  1216.           fputc(((r>>5)<<5) | ((g>>5)<<2) | (b>>6), fp);
  1217.  
  1218.           rerr = r - ( (r>>5) * 255 ) / 7;
  1219.           gerr = g - ( (g>>5) * 255 ) / 7;
  1220.           berr = b - ( (b>>6) * 255 ) / 3;
  1221.         }
  1222.         }
  1223.       break;
  1224.  
  1225.     case GRAY:
  1226.       for (i=0; i<h; i++)
  1227.         {
  1228.           /* Do a cheap unidirectional error-spread to look better */
  1229.           gint b3err=0, b2err=0, v, a;
  1230.  
  1231.           for (j=0; j<w; j++)
  1232.         {
  1233.           gint32 b3, b2;
  1234.  
  1235.           v = *(tbd++);
  1236.           a = *(tbd++);
  1237.  
  1238.           if (!(128 & a))
  1239.             v = (( (i^j) & 4 ) << 5) | 64;
  1240.  
  1241.           b2 = v + b2err;
  1242.           b3 = v + b3err;
  1243.  
  1244.           b2 = CLAMP0255 (b2);
  1245.           b3 = CLAMP0255 (b3);
  1246.  
  1247.           fputc(((b3>>5)<<5) | ((b3>>5)<<2) | (b2>>6), fp);
  1248.  
  1249.           b2err = b2 - ( (b2>>6) * 255 ) / 3;
  1250.           b3err = b3 - ( (b3>>5) * 255 ) / 7;
  1251.         }
  1252.         }
  1253.       break;
  1254.  
  1255.     default:
  1256.       g_warning("UNKNOWN GIMAGE TYPE IN THUMBNAIL SAVE");
  1257.       break;
  1258.     }
  1259.  
  1260.       fclose (fp);
  1261.     }
  1262.   else /* Error writing thumbnail */
  1263.     {
  1264.       return FALSE;
  1265.     }
  1266.  
  1267.   return TRUE;
  1268. }
  1269.  
  1270. static gint
  1271. file_save (GimpImage   *gimage,
  1272.        gchar       *filename,
  1273.        gchar       *raw_filename,
  1274.            RunModeType  run_mode)
  1275. {
  1276.   PlugInProcDef *file_proc;
  1277.   ProcRecord *proc;
  1278.   Argument   *args;
  1279.   Argument   *return_vals;
  1280.   gint status;
  1281.   gint i;
  1282.   struct stat statbuf;
  1283.  
  1284. #ifdef GDK_USE_UTF8_MBS
  1285.   ASSERT_NOT_UTF8 (filename);
  1286.   ASSERT_NOT_UTF8 (raw_filename);
  1287. #endif
  1288.  
  1289.   if (gimage_active_drawable (gimage) == NULL)
  1290.     return PDB_EXECUTION_ERROR;
  1291.  
  1292.   file_proc = gimage_get_save_proc (gimage);
  1293.  
  1294.   if (!file_proc)
  1295.     file_proc = file_proc_find (save_procs, raw_filename);
  1296.  
  1297.   if (!file_proc)
  1298.     {
  1299. #ifdef GDK_USE_UTF8_MBS
  1300.       gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  1301.                          NULL, NULL, NULL);
  1302.       g_message (_("Save failed.\n"
  1303.            "%s: Unknown file type."),
  1304.          utf8_filename);
  1305.       g_free (utf8_filename);
  1306. #else
  1307.       g_message (_("Save failed.\n"
  1308.            "%s: Unknown file type."),
  1309.          filename);
  1310. #endif
  1311.  
  1312.       return PDB_CANCEL;  /* inhibits error messages by caller */
  1313.     }
  1314.  
  1315.   /* check if we are saving to a file */
  1316.   if (stat (filename, &statbuf) == 0)
  1317.     {
  1318.       uid_t euid;
  1319.       gid_t egid;
  1320.  
  1321.       if (! (statbuf.st_mode & S_IFREG))
  1322.         {
  1323. #ifdef GDK_USE_UTF8_MBS
  1324.       gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  1325.                              NULL, NULL, NULL);
  1326.       g_message (_("Save failed.\n"
  1327.                "%s is not a regular file."),
  1328.              utf8_filename);
  1329.       g_free (utf8_filename);
  1330. #else
  1331.       g_message (_("Save failed.\n"
  1332.                "%s is not a regular file."),
  1333.              filename);
  1334. #endif
  1335.  
  1336.           return PDB_CANCEL;  /* inhibits error messages by caller */
  1337.         }
  1338.  
  1339.       euid = geteuid ();
  1340.       egid = getegid ();
  1341.  
  1342.       if (! ((statbuf.st_mode & S_IWUSR) ||
  1343.  
  1344.              ((statbuf.st_mode & S_IWGRP) &&
  1345.               (statbuf.st_uid != euid)) ||
  1346.  
  1347.              ((statbuf.st_mode & S_IWOTH) &&
  1348.               (statbuf.st_uid != euid) &&
  1349.               (statbuf.st_gid != egid))))
  1350.         {
  1351. #ifdef GDK_USE_UTF8_MBS
  1352.       gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  1353.                              NULL, NULL, NULL);
  1354.       g_message (_("Save failed.\n"
  1355.                "%s: Permission denied."),
  1356.              utf8_filename);
  1357.       g_free (utf8_filename);
  1358. #else
  1359.       g_message (_("Save failed.\n"
  1360.                "%s: Permission denied."),
  1361.              filename);
  1362. #endif
  1363.  
  1364.           return PDB_CANCEL;  /* inhibits error messages by caller */
  1365.         }
  1366.     }
  1367.  
  1368.   /* ref the image, so it can't get deleted during save */
  1369.   gtk_object_ref (GTK_OBJECT (gimage));
  1370.  
  1371.   proc = &file_proc->db_info;
  1372.  
  1373.   args = g_new0 (Argument, proc->num_args);
  1374.  
  1375.   for (i = 0; i < proc->num_args; i++)
  1376.     args[i].arg_type = proc->args[i].arg_type;
  1377.  
  1378.   args[0].value.pdb_int     = run_mode;
  1379.   args[1].value.pdb_int     = pdb_image_to_id (gimage);
  1380.   args[2].value.pdb_int     = drawable_ID (gimage_active_drawable (gimage));
  1381.   args[3].value.pdb_pointer = filename;
  1382.   args[4].value.pdb_pointer = raw_filename;
  1383.  
  1384.   return_vals = procedural_db_execute (proc->name, args);
  1385.  
  1386.   status = return_vals[0].value.pdb_int;
  1387.  
  1388.   if (status == PDB_SUCCESS)
  1389.     {
  1390.       /*  set this image to clean  */
  1391.       gimage_clean_all (gimage);
  1392.  
  1393.       /* these calls must come before the call to gimage_set_filename */
  1394.       document_index_add (filename);
  1395.       menus_last_opened_add (filename);
  1396.  
  1397.       /*  use the same plug-in for this image next time  */
  1398.       /* DISABLED - gets stuck on first saved format... needs
  1399.      attention --Adam */
  1400.       /* gimage_set_save_proc(gimage, file_proc); */
  1401.  
  1402.       /* Write a thumbnail for the saved image, where appropriate */
  1403.       switch (thumbnail_mode)
  1404.     {
  1405.     case 0:
  1406.       break;
  1407.     default:
  1408.       {
  1409.         TempBuf *tempbuf;
  1410.  
  1411.         tempbuf = make_thumb_tempbuf (gimage);
  1412.         file_save_thumbnail (gimage, filename, tempbuf);
  1413.       }
  1414.     }
  1415.  
  1416.       /*  set the image title  */
  1417.       gimp_image_set_filename (gimage, filename);
  1418.       /* note: 'filename' may have been free'd by above call! */
  1419.     }
  1420.  
  1421.   g_free (return_vals);
  1422.   g_free (args);
  1423.  
  1424.   gtk_object_unref (GTK_OBJECT (gimage));
  1425.  
  1426.   return status;
  1427. }
  1428.  
  1429. /* The readXVThumb function source may be re-used under
  1430.    the XFree86-style license. <adam@gimp.org> */
  1431. guchar *
  1432. readXVThumb (const gchar  *fnam,
  1433.          gint         *w,
  1434.          gint         *h,
  1435.          gchar       **imginfo /* caller frees if != NULL */)
  1436. {
  1437.   FILE *fp;
  1438.   const gchar *P7_332 = "P7 332";
  1439.   gchar P7_buf[7];
  1440.   gchar linebuf[200];
  1441.   guchar *buf;
  1442.   gint twofivefive;
  1443.   void *ptr;
  1444.  
  1445. #ifdef GDK_USE_UTF8_MBS
  1446.   ASSERT_NOT_UTF8 (fnam);
  1447. #endif
  1448.  
  1449.   *w = *h = 0;
  1450.   *imginfo = NULL;
  1451.  
  1452.   fp = fopen (fnam, "rb");
  1453.   if (!fp)
  1454.     return NULL;
  1455.  
  1456.   fread (P7_buf, 6, 1, fp);
  1457.  
  1458.   if (strncmp(P7_buf, P7_332, 6)!=0)
  1459.     {
  1460.       g_warning ("Thumbnail doesn't have the 'P7 332' header.");
  1461.       fclose (fp);
  1462.       return NULL;
  1463.     }
  1464.  
  1465.   /*newline*/
  1466.   fread (P7_buf, 1, 1, fp);
  1467.  
  1468.   do
  1469.     {
  1470.       ptr = fgets(linebuf, 199, fp);
  1471.       if ((strncmp(linebuf, "#IMGINFO:", 9) == 0) &&
  1472.       (linebuf[9] != '\0') &&
  1473.       (linebuf[9] != '\n'))
  1474.     {
  1475.       if (linebuf[strlen(linebuf)-1] == '\n')
  1476.         linebuf[strlen(linebuf)-1] = '\0';
  1477.  
  1478.       if (linebuf[9] != '\0')
  1479.         {
  1480.           if (*imginfo)
  1481.         g_free(*imginfo);
  1482.           *imginfo = g_strdup (&linebuf[9]);
  1483.         }
  1484.     }
  1485.     }
  1486.   while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */
  1487.  
  1488.   if (!ptr)
  1489.     {
  1490.       /* g_warning("Thumbnail ended - not an image?"); */
  1491.       fclose (fp);
  1492.       return NULL;
  1493.     }
  1494.  
  1495.   sscanf(linebuf, "%d %d %d\n", w, h, &twofivefive);
  1496.  
  1497.   if (twofivefive!=255)
  1498.     {
  1499.       g_warning ("Thumbnail is of funky depth.");
  1500.       fclose (fp);
  1501.       return NULL;
  1502.     }
  1503.  
  1504.   if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60)
  1505.     {
  1506.       g_warning ("Thumbnail size bad.  Corrupted?");
  1507.       fclose (fp);
  1508.       return NULL;
  1509.     }
  1510.  
  1511.   buf = g_malloc ((*w) * (*h));
  1512.  
  1513.   fread (buf, (*w) * (*h), 1, fp);
  1514.  
  1515.   fclose (fp);
  1516.   
  1517.   return buf;
  1518. }
  1519.  
  1520. /* don't call with preview_fullname as parameter!  will be clobbered! */
  1521. static void
  1522. set_preview (const gchar *fullfname,
  1523.          guchar      *RGB_source,
  1524.          gint         RGB_w,
  1525.          gint         RGB_h)
  1526. {
  1527.   guchar *thumb_rgb;
  1528.   guchar *raw_thumb;
  1529.   gint    tnw,tnh, i;
  1530.   gchar  *pname;
  1531.   gchar  *fname;
  1532.   gchar  *tname;
  1533.   gchar  *imginfo = NULL;
  1534.   struct stat file_stat;
  1535.   struct stat thumb_stat;
  1536.   gboolean thumb_may_be_outdated = FALSE;
  1537.   gboolean show_generate_label = TRUE;
  1538.  
  1539. #ifdef GDK_USE_UTF8_MBS
  1540.   gchar *utf8_fullfname;
  1541.   gchar *utf8_fname;
  1542.  
  1543.   ASSERT_NOT_UTF8 (fullfname);
  1544.  
  1545.   utf8_fullfname = g_filename_to_utf8 (fullfname, -1, NULL, NULL, NULL);
  1546.   utf8_fname = g_basename (utf8_fullfname);
  1547. #endif
  1548.  
  1549.   pname = g_dirname (fullfname);
  1550.   fname = g_basename (fullfname); /* Don't free this! */
  1551.   tname = g_strconcat (pname, G_DIR_SEPARATOR_S,
  1552.                ".xvpics", G_DIR_SEPARATOR_S,
  1553.                fname, NULL);
  1554.  
  1555.   g_free (pname);
  1556.  
  1557.   /*  If the file is newer than its thumbnail, the thumbnail may
  1558.    *  be out of date.
  1559.    */
  1560.   if ((stat (tname,     &thumb_stat) == 0) &&
  1561.       (stat (fullfname, &file_stat ) == 0))
  1562.     {
  1563.       if ((thumb_stat.st_mtime) < (file_stat.st_mtime))
  1564.     {
  1565.       thumb_may_be_outdated = TRUE;
  1566.     }
  1567.     }
  1568.  
  1569.   raw_thumb = readXVThumb (tname, &tnw, &tnh, &imginfo);
  1570.  
  1571.   g_free (tname);
  1572.  
  1573. #ifdef GDK_USE_UTF8_MBS
  1574.   gtk_frame_set_label (GTK_FRAME (open_options_frame), utf8_fname);
  1575.   g_free (utf8_fullfname);
  1576. #else
  1577.   gtk_frame_set_label (GTK_FRAME (open_options_frame), fname);
  1578. #endif
  1579.  
  1580.   g_free (preview_fullname);
  1581.   preview_fullname = g_strdup (fullfname);
  1582.  
  1583.   if (RGB_source)
  1584.     {
  1585.       gtk_preview_size (open_options_preview, RGB_w, RGB_h);
  1586.       
  1587.       for (i = 0; i < RGB_h; i++)
  1588.     {
  1589.       gtk_preview_draw_row (open_options_preview, &RGB_source[3*i*RGB_w],
  1590.                 0, i,
  1591.                 RGB_w);
  1592.     }
  1593.     }
  1594.   else
  1595.     {
  1596.       if (raw_thumb)
  1597.     {
  1598.       thumb_rgb = g_malloc (3 * tnw * tnh);
  1599.  
  1600.       for (i = 0; i < tnw * tnh; i++)
  1601.         {
  1602.           thumb_rgb[i*3  ] = ((raw_thumb[i]>>5)*255)/7;
  1603.           thumb_rgb[i*3+1] = (((raw_thumb[i]>>2)&7)*255)/7;
  1604.           thumb_rgb[i*3+2] = (((raw_thumb[i])&3)*255)/3;
  1605.         }
  1606.  
  1607.       gtk_preview_size (open_options_preview, tnw, tnh);
  1608.  
  1609.       for (i = 0; i < tnh; i++)
  1610.         {
  1611.           gtk_preview_draw_row (open_options_preview, &thumb_rgb[3*i*tnw],
  1612.                     0, i,
  1613.                     tnw);
  1614.         }
  1615.  
  1616.       g_free (thumb_rgb);
  1617.     }
  1618.     }
  1619.  
  1620.   if (raw_thumb || RGB_source)  /* We can show *some* kind of preview. */
  1621.     {
  1622.       if (raw_thumb) /* Managed to commit thumbnail file to disk */
  1623.     {
  1624.       gtk_label_set_text (GTK_LABEL (open_options_label),
  1625.                   thumb_may_be_outdated ?
  1626.                   _("(This thumbnail may be out of date)") :
  1627.                   (imginfo ? imginfo : _("(No Information)")));
  1628.       if (imginfo)
  1629.         g_free (imginfo);
  1630.     }
  1631.       else
  1632.     {
  1633.       switch (thumbnail_mode)
  1634.         {
  1635.         case 0:
  1636.           gtk_label_set_text (GTK_LABEL(open_options_label),
  1637.                   _("(Thumbnail saving is disabled)"));
  1638.           break;
  1639.         case 1:
  1640.           gtk_label_set_text (GTK_LABEL(open_options_label),
  1641.                   _("(Could not write thumbnail file)"));
  1642.           break;
  1643.         default:
  1644.           gtk_label_set_text (GTK_LABEL(open_options_label),
  1645.                   _("(Thumbnail file not written)"));
  1646.         }
  1647.     }
  1648.  
  1649.       gtk_widget_show (GTK_WIDGET (open_options_preview));
  1650.       gtk_widget_queue_draw (GTK_WIDGET(open_options_preview));
  1651.  
  1652.       show_generate_label = FALSE;
  1653.       
  1654.       g_free (raw_thumb);
  1655.     }
  1656.   else
  1657.     {
  1658.       if (imginfo)
  1659.     g_free (imginfo);
  1660.  
  1661.       gtk_widget_hide (GTK_WIDGET (open_options_preview));
  1662.       gtk_label_set_text (GTK_LABEL (open_options_label),
  1663.               _("No preview available"));
  1664.     }
  1665.  
  1666.   if (show_generate_label)
  1667.     {
  1668.       gtk_widget_hide (GTK_WIDGET (open_options_preview));
  1669.       gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel));
  1670.     }
  1671.   else
  1672.     {
  1673.       gtk_widget_hide (GTK_WIDGET (open_options_genbuttonlabel));
  1674.       gtk_widget_show (GTK_WIDGET (open_options_preview));
  1675.     }
  1676. }
  1677.  
  1678. static void
  1679. file_open_clistrow_callback (GtkWidget *widget,
  1680.                  gint       row)
  1681. {
  1682.   gchar *fullfname = NULL;
  1683.  
  1684.   fullfname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fileload));
  1685.   
  1686.   gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), TRUE);
  1687.   set_preview (fullfname, NULL, 0, 0);
  1688. }
  1689.  
  1690. static void
  1691. file_open_genbutton_callback (GtkWidget *widget,
  1692.                   gpointer   data)
  1693. {
  1694.   GimpImage *gimage_to_be_thumbed;
  1695.   guchar    *RGBbuf;
  1696.   TempBuf   *tempbuf;
  1697.   gint       RGBbuf_w;
  1698.   gint       RGBbuf_h;
  1699.  
  1700.   /* added for multi-file preview generation... */
  1701.   GSList *list, *toplist;
  1702.   GtkFileSelection *fs;
  1703.   gchar *full_filename = NULL;
  1704.   gchar *dirname;
  1705.   struct stat buf;
  1706.   gint err;
  1707.  
  1708.   fs = GTK_FILE_SELECTION (data);
  1709.  
  1710.   if (!preview_fullname)
  1711.     {
  1712.       g_warning ("Tried to generate thumbnail for NULL filename.");
  1713.       return;
  1714.     }
  1715.  
  1716.   gimp_add_busy_cursors ();
  1717.   gtk_widget_set_sensitive (GTK_WIDGET (fileload), FALSE);
  1718.  
  1719.   /* Have to read the clist before touching anything else */
  1720.   toplist = clist_to_slist (GTK_CLIST(fs->file_list));
  1721.     
  1722.   dirname = g_dirname (gtk_file_selection_get_filename (fs));
  1723.  
  1724.   for (list = toplist; list; list = g_slist_next (list))
  1725.     {
  1726. #ifdef GDK_USE_UTF8_MBS
  1727.       gchar *tmp = g_filename_from_utf8 ((gchar *) list->data, -1, NULL, NULL, NULL);
  1728.       full_filename = g_strconcat (dirname, G_DIR_SEPARATOR_S, tmp, NULL);
  1729.       g_free (tmp);
  1730. #else
  1731.       full_filename = g_strconcat (dirname, G_DIR_SEPARATOR_S,
  1732.                                    (gchar *) list->data, NULL);
  1733. #endif
  1734.       err = stat (full_filename, &buf);
  1735.  
  1736.       
  1737.       if (! (err == 0 && (buf.st_mode & S_IFDIR)))
  1738.         { /* Is not directory. */
  1739.           gint dummy;
  1740.           
  1741.           gimage_to_be_thumbed = file_open_image (full_filename,
  1742.                                                   list->data,
  1743.                                                   NULL,
  1744.                                                   _("Open"),
  1745.                                                   RUN_NONINTERACTIVE,
  1746.                                                   &dummy);
  1747.           
  1748.           if (gimage_to_be_thumbed)
  1749.             {            
  1750.               tempbuf = make_thumb_tempbuf (gimage_to_be_thumbed);
  1751.               RGBbuf  = make_RGBbuf_from_tempbuf (tempbuf,
  1752.                                                   &RGBbuf_w,
  1753.                                                   &RGBbuf_h);
  1754.               if (thumbnail_mode)
  1755.                 {
  1756.                   file_save_thumbnail (gimage_to_be_thumbed,
  1757.                                        full_filename, tempbuf);
  1758.                 }
  1759.               set_preview (full_filename, RGBbuf, RGBbuf_w, RGBbuf_h);
  1760.               
  1761.               gimage_delete (gimage_to_be_thumbed);
  1762.               
  1763.               if (RGBbuf)
  1764.                 g_free (RGBbuf);
  1765.             }
  1766.           else
  1767.             {
  1768.               gtk_label_set_text (GTK_LABEL (open_options_label),
  1769.                                   _("(could not make preview)"));
  1770.             }
  1771.         }
  1772.       
  1773.       g_free (full_filename);
  1774.     }
  1775.  
  1776.   g_free (dirname);
  1777.  
  1778.   for (list = toplist; list; list = g_slist_next (list))
  1779.     g_free (list->data);
  1780.   
  1781.   g_slist_free (toplist);
  1782.  
  1783.   gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE);
  1784.   gimp_remove_busy_cursors (NULL);
  1785. }
  1786.  
  1787. static void
  1788. file_open_ok_callback (GtkWidget *widget,
  1789.                gpointer   data)
  1790. {
  1791.   GtkFileSelection *fs;
  1792.   gchar *full_filename, *raw_filename;
  1793.   gchar *dirname;
  1794.   struct stat buf;
  1795.   gint err;
  1796.   gint status;
  1797.  
  1798.   fs = GTK_FILE_SELECTION (data);
  1799.   full_filename = gtk_file_selection_get_filename (fs);
  1800.   raw_filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
  1801.  
  1802.   g_assert (full_filename && raw_filename);
  1803.  
  1804.   if (strlen (raw_filename) == 0)
  1805.     return;
  1806.  
  1807. #ifdef GDK_USE_UTF8_MBS
  1808.   raw_filename = g_filename_from_utf8 (raw_filename, -1, NULL, NULL, NULL);
  1809. #endif
  1810.  
  1811.   err = stat (full_filename, &buf);
  1812.  
  1813.   if (err == 0 && (buf.st_mode & S_IFDIR))
  1814.     {
  1815.       if (full_filename[strlen (full_filename) - 1] != G_DIR_SEPARATOR)
  1816.     {
  1817.       gchar *s = g_strconcat (full_filename, G_DIR_SEPARATOR_S, NULL);
  1818.       gtk_file_selection_set_filename (fs, s);
  1819.       g_free (s);
  1820.     }
  1821.       else
  1822.     gtk_file_selection_set_filename (fs, full_filename);
  1823.  
  1824.       return;
  1825.     }
  1826.  
  1827.   gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE);
  1828.  
  1829.   if (err) /* e.g. http://server/filename.jpg */
  1830.     full_filename = raw_filename;
  1831.  
  1832.   /* use the load procedure set by the dialog */
  1833.   status = file_open_with_proc (full_filename,
  1834.                                 raw_filename,
  1835.                                 load_file_proc);
  1836.  
  1837.   if (status == PDB_SUCCESS)
  1838.     {
  1839.       file_dialog_hide (data);
  1840.     }
  1841.   else if (status != PDB_CANCEL)
  1842.     {
  1843. #ifdef GDK_USE_UTF8_MBS
  1844.       gchar *utf8_full_filename = g_filename_to_utf8 (full_filename, -1,
  1845.                               NULL, NULL, NULL);
  1846.       g_message (_("Open failed.\n%s"), utf8_full_filename);
  1847.       g_free (utf8_full_filename);
  1848. #else
  1849.       g_message (_("Open failed.\n%s"), full_filename);
  1850. #endif
  1851.     }
  1852.  
  1853.   /*
  1854.    * Now deal with multiple selections from the filesel clist
  1855.    */
  1856.  
  1857.   {
  1858.     GSList *list;
  1859.  
  1860.     /* Have to read the clist before touching anything else */
  1861.  
  1862.     list = clist_to_slist (GTK_CLIST (fs->file_list));
  1863.  
  1864. #ifndef GDK_USE_UTF8_MBS
  1865.     raw_filename = g_strdup (raw_filename);
  1866. #endif
  1867.     dirname = g_dirname (gtk_file_selection_get_filename (fs));
  1868.  
  1869.     while (list)
  1870.       {
  1871. #ifdef GDK_USE_UTF8_MBS
  1872.     gchar *tmp = g_filename_from_utf8 ((gchar *) list->data, -1,
  1873.                        NULL, NULL, NULL);
  1874.     full_filename = g_strconcat (dirname, G_DIR_SEPARATOR_S, tmp, NULL);
  1875.         if (strcmp (tmp, raw_filename))
  1876. #else
  1877.         full_filename = g_strconcat (dirname, G_DIR_SEPARATOR_S,
  1878.                      (gchar *) list->data, NULL);
  1879.         if (strcmp (list->data, raw_filename))
  1880. #endif
  1881.           { /* don't load current selection twice */
  1882.  
  1883.             err = stat (full_filename, &buf);
  1884.  
  1885.             if (! (err == 0 && (buf.st_mode & S_IFDIR)))
  1886.               { /* Is not directory. */
  1887.  
  1888.                 /* use the load procedure set by the dialog */
  1889.                 status = file_open_with_proc (full_filename,
  1890.                                               raw_filename,
  1891.                                               load_file_proc);
  1892.  
  1893.                 if (status == PDB_SUCCESS)
  1894.                   {
  1895.                     file_dialog_hide (data);
  1896.                   }
  1897.                 else if (status != PDB_CANCEL)
  1898.                   {
  1899. #ifdef GDK_USE_UTF8_MBS
  1900.             gchar *utf8_full_filename =
  1901.               g_filename_to_utf8 (full_filename, -1, NULL, NULL, NULL);
  1902.                     g_message (_("Open failed.\n%s"), utf8_full_filename);
  1903.             g_free (utf8_full_filename);
  1904. #else
  1905.                     g_message (_("Open failed.\n%s"), full_filename);
  1906. #endif
  1907.                   }
  1908.               }
  1909.           }
  1910.      
  1911.         g_free (full_filename);
  1912.         g_free (list->data);
  1913.         list = g_slist_next (list);
  1914. #ifdef GDK_USE_UTF8_MBS
  1915.     g_free (tmp);
  1916. #endif
  1917.       }
  1918.  
  1919.     g_slist_free (list);
  1920.     list = NULL;
  1921.   }    
  1922.   g_free (raw_filename);
  1923.   g_free (dirname);
  1924.  
  1925.   gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE);
  1926. }
  1927.  
  1928. static GSList *
  1929. clist_to_slist (GtkCList *file_list)
  1930. {
  1931.   GSList *list = NULL;
  1932.   GList  *row;
  1933.   gint    rownum;
  1934.   gchar  *temp;
  1935.   
  1936.   for (row = file_list->row_list, rownum = 0;
  1937.        row; 
  1938.        row = g_list_next (row), rownum++)
  1939.     {
  1940.       if (GTK_CLIST_ROW (row)->state == GTK_STATE_SELECTED)
  1941.         {
  1942.           if (gtk_clist_get_cell_type (file_list, rownum, 0) == GTK_CELL_TEXT)
  1943.             {
  1944.               gtk_clist_get_text (file_list, rownum, 0, &temp);
  1945.               list = g_slist_prepend (list, g_strdup (temp));
  1946.             }
  1947.         }
  1948.     }
  1949.  
  1950.   return list;
  1951. }
  1952.  
  1953.  
  1954. /* Set "gimage"s save handler to "save_proc", then save the image.  
  1955.  * Hide the dialog if all went well, otherwise make the user knows an
  1956.  * error happened and leave the dialog up.  Make sure it's sensitive.
  1957.  */
  1958. static void
  1959. file_save_with_proc (GImage *gimage,
  1960.              gchar *full_filename,
  1961.              gchar *raw_filename,
  1962.              PlugInProcDef *save_proc)
  1963. {
  1964.     gint status = PDB_EXECUTION_ERROR;
  1965.  
  1966. #ifdef GDK_USE_UTF8_MBS
  1967.     ASSERT_NOT_UTF8 (full_filename);
  1968.     ASSERT_NOT_UTF8 (raw_filename);
  1969. #endif
  1970.  
  1971.     if (gimage != NULL)
  1972.       {
  1973.     gimage_set_save_proc (gimage, save_proc);
  1974.     status = file_save (gimage,
  1975.                 full_filename,
  1976.                 raw_filename,
  1977.                 RUN_INTERACTIVE);
  1978.  
  1979.     /* hide the file save dialog on success */
  1980.     if (status == PDB_SUCCESS)
  1981.       file_dialog_hide (filesave);
  1982.       }
  1983.  
  1984.     /* If there was an error but file_save() didn't print an error
  1985.      * message, then we'd better. */
  1986.     if (status != PDB_SUCCESS && status != PDB_CANCEL)
  1987.       {
  1988. #ifdef GDK_USE_UTF8_MBS
  1989.     gchar *utf8_full_filename = g_filename_to_utf8 (full_filename, -1,
  1990.                             NULL, NULL, NULL);
  1991.     g_message (_("Save failed.\n%s"), utf8_full_filename);
  1992.     g_free (utf8_full_filename);
  1993. #else
  1994.     g_message (_("Save failed.\n%s"), full_filename);
  1995. #endif
  1996.       }
  1997.      
  1998.     /* always make file save dialog sensitive */
  1999.     gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE);
  2000. }
  2001.  
  2002.  
  2003. static void
  2004. file_save_ok_callback (GtkWidget *widget,
  2005.                gpointer   data)
  2006. {
  2007.   GtkFileSelection *fs;
  2008.   gchar       *filename;
  2009.   gchar       *raw_filename;
  2010.   gchar       *dot;
  2011.   gint         x;
  2012.   struct stat  buf;
  2013.   gint         err;
  2014.  
  2015.   fs = GTK_FILE_SELECTION (data);
  2016.   filename = gtk_file_selection_get_filename (fs);
  2017.   raw_filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
  2018.  
  2019.   g_assert (filename && raw_filename);
  2020.  
  2021. #ifdef GDK_USE_UTF8_MBS
  2022.   {
  2023.     gchar *tmp = raw_filename;
  2024.     raw_filename = g_filename_from_utf8 (raw_filename, -1, NULL, NULL, NULL);
  2025.     g_free (tmp);
  2026.   }
  2027. #endif
  2028.  
  2029.   for (dot = strrchr (filename, '.'), x = 0; dot && *(++dot);)
  2030.     {
  2031.       if (*dot != 'e' || ++x < 0)
  2032.     break;
  2033.       else if (x > 3 && !strcmp (dot + 1, "k"))
  2034.     { 
  2035.       ProcRecord   *proc_rec;
  2036.       Argument     *args;
  2037.       GimpDrawable *the_drawable;
  2038.  
  2039.       the_drawable = gimp_image_active_drawable (the_gimage);
  2040.       if (!the_drawable)
  2041.         return;
  2042.  
  2043.       proc_rec = procedural_db_lookup ("plug_in_the_slimy_egg");
  2044.       if (!proc_rec)
  2045.         break;
  2046.  
  2047.       file_dialog_hide (filesave);
  2048.  
  2049.       args = g_new (Argument, 3);
  2050.       args[0].arg_type      = PDB_INT32;
  2051.       args[0].value.pdb_int = RUN_INTERACTIVE;
  2052.       args[1].arg_type      = PDB_IMAGE;
  2053.       args[1].value.pdb_int = pdb_image_to_id (the_gimage);
  2054.       args[2].arg_type      = PDB_DRAWABLE;
  2055.       args[2].value.pdb_int = the_drawable->ID;
  2056.  
  2057.       plug_in_run (proc_rec, args, 3, FALSE, TRUE, 0);
  2058.  
  2059.       g_free (args);
  2060.       
  2061.       return;
  2062.     }
  2063.    }
  2064.  
  2065.   err = stat (filename, &buf);
  2066.  
  2067.   if (err == 0)
  2068.     {
  2069.       if (buf.st_mode & S_IFDIR)
  2070.     {
  2071.       if (filename[strlen (filename) - 1] != G_DIR_SEPARATOR)
  2072.         {
  2073.           gchar *s = g_strconcat (filename, G_DIR_SEPARATOR_S, NULL);
  2074.           gtk_file_selection_set_filename (fs, s);
  2075.           g_free (s);
  2076.         }
  2077.       else
  2078.         gtk_file_selection_set_filename (fs, filename);
  2079.     }
  2080.       else
  2081.     {
  2082.       gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE);
  2083.       file_overwrite (GTK_WIDGET (fs),
  2084.               g_strdup (filename), g_strdup (raw_filename));
  2085.     }
  2086.     }
  2087.   else
  2088.     {
  2089.       gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE);
  2090.  
  2091.       file_save_with_proc (the_gimage, filename, raw_filename, save_file_proc);
  2092.  
  2093.       gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE);
  2094.     }
  2095. }
  2096.  
  2097. static void
  2098. file_dialog_show (GtkWidget *filesel)
  2099. {
  2100.   menus_set_sensitive ("<Toolbox>/File/Open...", FALSE);
  2101.   menus_set_sensitive ("<Image>/File/Open...", FALSE);
  2102.   menus_set_sensitive ("<Image>/File/Save", FALSE);
  2103.   menus_set_sensitive ("<Image>/File/Save As...", FALSE);
  2104.  
  2105.   gtk_widget_grab_focus (GTK_FILE_SELECTION (filesel)->selection_entry);
  2106.   gtk_widget_show (filesel);
  2107. }
  2108.  
  2109. static int
  2110. file_dialog_hide (GtkWidget *filesel)
  2111. {
  2112.   gimp_dialog_hide (filesel);
  2113.   
  2114.   menus_set_sensitive ("<Toolbox>/File/Open...", TRUE);
  2115.   menus_set_sensitive ("<Image>/File/Open...", TRUE);
  2116.  
  2117.   if (gdisplay_active ())
  2118.     {
  2119.       menus_set_sensitive ("<Image>/File/Save", TRUE);
  2120.       menus_set_sensitive ("<Image>/File/Save As...", TRUE);
  2121.     }
  2122.  
  2123.   return TRUE;
  2124. }
  2125.  
  2126. static void
  2127. file_overwrite (GtkWidget *parent,
  2128.         gchar     *filename,
  2129.         gchar     *raw_filename)
  2130. {
  2131.   OverwriteData *overwrite_data;
  2132.   GtkWidget *query_box;
  2133.   gchar     *overwrite_text;
  2134.  
  2135.   overwrite_data = g_new (OverwriteData, 1);
  2136.   overwrite_data->full_filename = filename;
  2137.   overwrite_data->raw_filename  = raw_filename;
  2138.  
  2139. #ifdef GDK_USE_UTF8_MBS
  2140.  
  2141.   ASSERT_NOT_UTF8 (filename);
  2142.   ASSERT_NOT_UTF8 (raw_filename);
  2143.  
  2144.   {
  2145.     gchar *utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
  2146.     overwrite_text = g_strdup_printf (_("%s exists, overwrite?"), utf8_filename);
  2147.     g_free (utf8_filename);
  2148.   }
  2149. #else 
  2150.   overwrite_text = g_strdup_printf (_("%s exists, overwrite?"), filename);
  2151. #endif
  2152.   query_box = gimp_query_boolean_box (_("File Exists!"),
  2153.                       gimp_standard_help_func,
  2154.                       "save/file_exists.html",
  2155.                       FALSE,
  2156.                       overwrite_text,
  2157.                       _("Yes"), _("No"),
  2158.                       NULL, NULL,
  2159.                       file_overwrite_callback,
  2160.                       overwrite_data);
  2161.  
  2162.   gtk_window_set_transient_for (GTK_WINDOW (query_box), GTK_WINDOW (parent));
  2163.  
  2164.   g_free (overwrite_text);
  2165.  
  2166.   gtk_widget_show (query_box);
  2167. }
  2168.  
  2169. static void
  2170. file_overwrite_callback (GtkWidget *widget,
  2171.              gboolean   overwrite,
  2172.              gpointer   data)
  2173. {
  2174.   OverwriteData *overwrite_data;
  2175.  
  2176.   overwrite_data = (OverwriteData *) data;
  2177.  
  2178.   if (overwrite)
  2179.     {
  2180.       file_save_with_proc (the_gimage,
  2181.                overwrite_data->full_filename,
  2182.                overwrite_data->raw_filename,
  2183.                save_file_proc);
  2184.     }
  2185.  
  2186.   /* always make file save dialog sensitive */
  2187.   gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE);
  2188.  
  2189.   g_free (overwrite_data->full_filename);
  2190.   g_free (overwrite_data->raw_filename);
  2191.   g_free (overwrite_data);
  2192. }
  2193.  
  2194. static void
  2195. file_revert_confirm_callback (GtkWidget *widget,
  2196.                   gboolean   revert,
  2197.                   gpointer   data)
  2198. {
  2199.   GimpImage *old_gimage;
  2200.  
  2201.   old_gimage = (GimpImage *) data;
  2202.  
  2203.   gtk_object_set_data (GTK_OBJECT (old_gimage), REVERT_DATA_KEY, NULL);
  2204.  
  2205.   if (revert)
  2206.     {
  2207.       GimpImage *new_gimage;
  2208.       gchar     *filename;
  2209.       gint       status;
  2210.  
  2211.       filename = gimage_filename (old_gimage);
  2212.  
  2213. #ifdef GDK_USE_UTF8_MBS
  2214.       {
  2215.     gchar *tmp = filename;
  2216.     filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
  2217.     g_free (tmp);
  2218.       }
  2219. #endif
  2220.  
  2221.       new_gimage = file_open_image (filename, filename, NULL,
  2222.                                     _("Revert"), RUN_INTERACTIVE, &status);
  2223.  
  2224.       if (new_gimage != NULL)
  2225.     {
  2226.       undo_free (new_gimage);
  2227.       gdisplays_reconnect (old_gimage, new_gimage);
  2228.       gdisplays_resize_cursor_label (new_gimage);
  2229.       gdisplays_update_full (new_gimage);
  2230.       gdisplays_shrink_wrap (new_gimage);
  2231.       gimp_image_clean_all (new_gimage);
  2232.     }
  2233.       else if (status != PDB_CANCEL)
  2234.     {
  2235. #ifdef GDK_USE_UTF8_MBS
  2236.       gchar *utf8_filename = g_filename_to_utf8 (filename, -1,
  2237.                              NULL, NULL, NULL);
  2238.       g_message (_("Revert failed.\n%s"), utf8_filename);
  2239.       g_free (utf8_filename);
  2240. #else
  2241.       g_message (_("Revert failed.\n%s"), filename);
  2242. #endif
  2243.     }
  2244.     }
  2245. }
  2246.  
  2247. static PlugInProcDef *
  2248. file_proc_find_by_name (GSList   *procs,
  2249.                 gchar    *filename,
  2250.                 gboolean  skip_magic)
  2251. {
  2252.   GSList *p;
  2253.   gchar  *ext = strrchr (filename, '.');
  2254.  
  2255.   if (ext)
  2256.     ext++;
  2257.  
  2258.   for (p = procs; p; p = g_slist_next (p))
  2259.     {
  2260.       PlugInProcDef *proc = p->data;
  2261.       GSList        *prefixes;
  2262.  
  2263.       if (skip_magic && proc->magics_list)
  2264.     continue;
  2265.  
  2266.       for (prefixes = proc->prefixes_list;
  2267.        prefixes;
  2268.        prefixes = g_slist_next (prefixes))
  2269.     {
  2270.       if (strncmp (filename, prefixes->data, strlen (prefixes->data)) == 0)
  2271.         return proc;
  2272.     }
  2273.      }
  2274.  
  2275.   for (p = procs; p; p = g_slist_next (p))
  2276.     {
  2277.       PlugInProcDef *proc = p->data;
  2278.       GSList        *extensions;
  2279.  
  2280.       for (extensions = proc->extensions_list;
  2281.        ext && extensions;
  2282.        extensions = g_slist_next (extensions))
  2283.     {
  2284.       gchar *p1 = ext;
  2285.       gchar *p2 = (gchar *) extensions->data;
  2286.  
  2287.           if (skip_magic && proc->magics_list)
  2288.         continue;
  2289.  
  2290.       while (*p1 && *p2)
  2291.         {
  2292.           if (tolower (*p1) != tolower (*p2))
  2293.         break;
  2294.  
  2295.           p1++;
  2296.           p2++;
  2297.         }
  2298.  
  2299.       if (!(*p1) && !(*p2))
  2300.         return proc;
  2301.     }
  2302.     }
  2303.  
  2304.   return NULL;
  2305. }
  2306.  
  2307. PlugInProcDef *
  2308. file_proc_find (GSList *procs,
  2309.         gchar  *filename)
  2310. {
  2311.   PlugInProcDef *file_proc;
  2312.   PlugInProcDef *size_matched_proc;
  2313.   GSList *all_procs = procs;
  2314.   FILE   *ifp = NULL;
  2315.   gint    head_size        = -2;
  2316.   gint    size_match_count = 0;
  2317.   gint    match_val;
  2318.   guchar  head[256];
  2319.  
  2320.   size_matched_proc = NULL;
  2321.  
  2322.   /* First, check magicless prefixes/suffixes */
  2323.   if ( (file_proc = file_proc_find_by_name (all_procs, filename, TRUE)) != NULL)
  2324.     return file_proc;
  2325.  
  2326.   /* Then look for magics */
  2327.   while (procs)
  2328.     {
  2329.       file_proc = procs->data;
  2330.       procs = procs->next;
  2331.  
  2332.       if (file_proc->magics_list)
  2333.         {
  2334.           if (head_size == -2)
  2335.             {
  2336.               head_size = 0;
  2337.               if ((ifp = fopen (filename, "rb")) != NULL)
  2338.                 head_size = fread ((gchar *) head, 1, sizeof (head), ifp);
  2339.             }
  2340.           if (head_size >= 4)
  2341.             {
  2342.               match_val = file_check_magic_list (file_proc->magics_list,
  2343.                                                  head_size, head, ifp);
  2344.               if (match_val == 2)  /* size match ? */
  2345.                 { /* Use it only if no other magic matches */
  2346.                   size_match_count++;
  2347.                   size_matched_proc = file_proc;
  2348.                 }
  2349.               else if (match_val)
  2350.                 {
  2351.                   fclose (ifp);
  2352.                   return (file_proc);
  2353.                 }
  2354.             }
  2355.         }
  2356.     }
  2357.   if (ifp) fclose (ifp);
  2358.   if (size_match_count == 1)
  2359.     return (size_matched_proc);
  2360.  
  2361.   /* As a last ditch, try matching by name */
  2362.   return file_proc_find_by_name (all_procs, filename, FALSE);
  2363. }
  2364.  
  2365. static void
  2366. file_convert_string (gchar *instr,
  2367.              gchar *outmem,
  2368.              gint   maxmem,
  2369.              gint  *nmem)
  2370. {
  2371.   /* Convert a string in C-notation to array of char */
  2372.   guchar *uin = (guchar *) instr;
  2373.   guchar *uout = (guchar *) outmem;
  2374.   guchar  tmp[5], *tmpptr;
  2375.   gint    k;
  2376.  
  2377.   while ((*uin != '\0') && ((((char *)uout) - outmem) < maxmem))
  2378.     {
  2379.       if (*uin != '\\')   /* Not an escaped character ? */
  2380.         {
  2381.           *(uout++) = *(uin++);
  2382.           continue;
  2383.         }
  2384.       if (*(++uin) == '\0')
  2385.         {
  2386.           *(uout++) = '\\';
  2387.           break;
  2388.         }
  2389.       switch (*uin)
  2390.         {
  2391.           case '0':  case '1':  case '2':  case '3': /* octal */
  2392.             for (tmpptr = tmp; (tmpptr-tmp) <= 3;)
  2393.               {
  2394.                 *(tmpptr++) = *(uin++);
  2395.                 if (   (*uin == '\0') || (!isdigit (*uin))
  2396.                     || (*uin == '8') || (*uin == '9'))
  2397.                   break;
  2398.               }
  2399.             *tmpptr = '\0';
  2400.             sscanf ((char *)tmp, "%o", &k);
  2401.             *(uout++) = k;
  2402.             break;
  2403.  
  2404.           case 'a': *(uout++) = '\a'; uin++; break;
  2405.           case 'b': *(uout++) = '\b'; uin++; break;
  2406.           case 't': *(uout++) = '\t'; uin++; break;
  2407.           case 'n': *(uout++) = '\n'; uin++; break;
  2408.           case 'v': *(uout++) = '\v'; uin++; break;
  2409.           case 'f': *(uout++) = '\f'; uin++; break;
  2410.           case 'r': *(uout++) = '\r'; uin++; break;
  2411.  
  2412.           default : *(uout++) = *(uin++); break;
  2413.         }
  2414.     }
  2415.   *nmem = ((gchar *) uout) - outmem;
  2416. }
  2417.  
  2418. static gchar *
  2419. file_absolute_filename (gchar *name)
  2420. {
  2421.   PlugInProcDef *proc;
  2422.   GSList *procs;
  2423.   GSList *prefixes;
  2424.   gchar  *absolute;
  2425.   gchar  *current;
  2426.  
  2427.   g_return_val_if_fail (name != NULL, NULL);
  2428.  
  2429.   /*  check for prefixes like http or ftp  */
  2430.   for (procs = load_procs; procs; procs = g_slist_next (procs))
  2431.     {
  2432.       proc = (PlugInProcDef *)procs->data;
  2433.  
  2434.       for (prefixes = proc->prefixes_list;
  2435.        prefixes;
  2436.        prefixes = g_slist_next (prefixes))
  2437.     {
  2438.       if (strncmp (name, prefixes->data, strlen (prefixes->data)) == 0)
  2439.         return g_strdup (name);
  2440.     }
  2441.      }
  2442.  
  2443.   if (g_path_is_absolute (name))
  2444.     return g_strdup (name);
  2445.   
  2446.   current = g_get_current_dir ();
  2447.   absolute = g_strconcat (current, G_DIR_SEPARATOR_S, name, NULL);
  2448.   g_free (current);
  2449.  
  2450.   return absolute;
  2451. }
  2452.  
  2453. static gint
  2454. file_check_single_magic (gchar  *offset,
  2455.                          gchar  *type,
  2456.                          gchar  *value,
  2457.                          gint    headsize,
  2458.                          guchar *file_head,
  2459.                          FILE   *ifp)
  2460.  
  2461. {
  2462.   /* Return values are 0: no match, 1: magic match, 2: size match */
  2463.   glong   offs;
  2464.   gulong  num_testval, num_operatorval;
  2465.   gulong  fileval;
  2466.   gint    numbytes, k, c = 0, found = 0;
  2467.   gchar  *num_operator_ptr, num_operator, num_test;
  2468.   guchar  mem_testval[256];
  2469.  
  2470.   /* Check offset */
  2471.   if (sscanf (offset, "%ld", &offs) != 1) return (0);
  2472.   if (offs < 0) return (0);
  2473.  
  2474.   /* Check type of test */
  2475.   num_operator_ptr = NULL;
  2476.   num_operator = '\0';
  2477.   num_test = '=';
  2478.   if (strncmp (type, "byte", 4) == 0)
  2479.     {
  2480.       numbytes = 1;
  2481.       num_operator_ptr = type+4;
  2482.     }
  2483.   else if (strncmp (type, "short", 5) == 0)
  2484.     {
  2485.       numbytes = 2;
  2486.       num_operator_ptr = type+5;
  2487.     }
  2488.   else if (strncmp (type, "long", 4) == 0)
  2489.     {
  2490.       numbytes = 4;
  2491.       num_operator_ptr = type+4;
  2492.     }
  2493.   else if (strncmp (type, "size", 4) == 0)
  2494.     {
  2495.       numbytes = 5;
  2496.     }
  2497.   else if (strcmp (type, "string") == 0)
  2498.     {
  2499.       numbytes = 0;
  2500.     }
  2501.   else return (0);
  2502.  
  2503.   /* Check numerical operator value if present */
  2504.   if (num_operator_ptr && (*num_operator_ptr == '&'))
  2505.     {
  2506.       if (isdigit (num_operator_ptr[1]))
  2507.         {
  2508.           if (num_operator_ptr[1] != '0')      /* decimal */
  2509.             sscanf (num_operator_ptr+1, "%ld", &num_operatorval);
  2510.           else if (num_operator_ptr[2] == 'x') /* hexadecimal */
  2511.             sscanf (num_operator_ptr+3, "%lx", &num_operatorval);
  2512.           else                                 /* octal */
  2513.             sscanf (num_operator_ptr+2, "%lo", &num_operatorval);
  2514.           num_operator = *num_operator_ptr;
  2515.         }
  2516.     }
  2517.  
  2518.   if (numbytes > 0)   /* Numerical test ? */
  2519.     {
  2520.       /* Check test value */
  2521.       if ((value[0] == '=') || (value[0] == '>') || (value[0] == '<'))
  2522.       {
  2523.         num_test = value[0];
  2524.         value++;
  2525.       }
  2526.       if (!isdigit (value[0])) return (0);
  2527.  
  2528.       /* 
  2529.        * to anybody reading this: is strtol's parsing behaviour (e.g. "0x" prefix)
  2530.        * broken on some systems or why do we do the base detection ourselves?
  2531.        * */
  2532.       if (value[0] != '0')      /* decimal */
  2533.         num_testval = strtol(value, NULL, 10);
  2534.       else if (value[1] == 'x') /* hexadecimal */
  2535.         num_testval = (unsigned long)strtoul(value+2, NULL, 16);
  2536.       else                      /* octal */
  2537.         num_testval = strtol(value+1, NULL, 8);
  2538.  
  2539.       fileval = 0;
  2540.       if (numbytes == 5)    /* Check for file size ? */
  2541.         {
  2542.       struct stat buf;
  2543.       
  2544.           if (fstat (fileno (ifp), &buf) < 0) return (0);
  2545.           fileval = buf.st_size;
  2546.         }
  2547.       else if (offs + numbytes <= headsize)  /* We have it in memory ? */
  2548.         {
  2549.           for (k = 0; k < numbytes; k++)
  2550.           fileval = (fileval << 8) | (long)file_head[offs+k];
  2551.         }
  2552.       else   /* Read it from file */
  2553.         {
  2554.           if (fseek (ifp, offs, SEEK_SET) < 0) return (0);
  2555.           for (k = 0; k < numbytes; k++)
  2556.             fileval = (fileval << 8) | (c = getc (ifp));
  2557.           if (c == EOF) return (0);
  2558.         }
  2559.       if (num_operator == '&')
  2560.         fileval &= num_operatorval;
  2561.  
  2562.       if (num_test == '<')
  2563.         found = (fileval < num_testval);
  2564.       else if (num_test == '>')
  2565.         found = (fileval > num_testval);
  2566.       else
  2567.         found = (fileval == num_testval);
  2568.  
  2569.       if (found && (numbytes == 5)) found = 2;
  2570.     }
  2571.   else if (numbytes == 0) /* String test */
  2572.     {
  2573.       file_convert_string ((char *)value, (char *)mem_testval,
  2574.                            sizeof (mem_testval), &numbytes);
  2575.       if (numbytes <= 0) return (0);
  2576.  
  2577.       if (offs + numbytes <= headsize)  /* We have it in memory ? */
  2578.         {
  2579.           found = (memcmp (mem_testval, file_head+offs, numbytes) == 0);
  2580.         }
  2581.       else   /* Read it from file */
  2582.         {
  2583.           if (fseek (ifp, offs, SEEK_SET) < 0) return (0);
  2584.           found = 1;
  2585.           for (k = 0; found && (k < numbytes); k++)
  2586.             {
  2587.               c = getc (ifp);
  2588.               found = (c != EOF) && (c == (int)mem_testval[k]);
  2589.             }
  2590.         }
  2591.     }
  2592.  
  2593.   return (found);
  2594. }
  2595.  
  2596. static int
  2597. file_check_magic_list (GSList *magics_list,
  2598.                gint    headsize,
  2599.                guchar *head,
  2600.                FILE   *ifp)
  2601.  
  2602. {
  2603.   /* Return values are 0: no match, 1: magic match, 2: size match */
  2604.   gchar *offset, *type, *value;
  2605.   gint and = 0;
  2606.   gint found = 0, match_val;
  2607.  
  2608.   while (magics_list)
  2609.     {
  2610.       if ((offset = (char *)magics_list->data) == NULL) break;
  2611.       if ((magics_list = magics_list->next) == NULL) break;
  2612.       if ((type = (char *)magics_list->data) == NULL) break;
  2613.       if ((magics_list = magics_list->next) == NULL) break;
  2614.       if ((value = (char *)magics_list->data) == NULL) break;
  2615.       magics_list = magics_list->next;
  2616.  
  2617.       match_val = file_check_single_magic (offset, type, value,
  2618.                                            headsize, head, ifp);
  2619.       if (and)
  2620.     found = found && match_val;
  2621.       else
  2622.     found = match_val;
  2623.  
  2624.       and = (strchr (offset, '&') != NULL);
  2625.       if ((!and) && found) return (match_val);
  2626.     }
  2627.   return (0);
  2628. }
  2629.  
  2630. static void
  2631. file_update_menus (GSList *procs,
  2632.            gint    image_type)
  2633. {
  2634.   PlugInProcDef *file_proc;
  2635.  
  2636.   while (procs)
  2637.     {
  2638.       file_proc = procs->data;
  2639.       procs = procs->next;
  2640.  
  2641.       if (file_proc->db_info.proc_type != PDB_EXTENSION)
  2642.     menus_set_sensitive (file_proc->menu_path,
  2643.                  (file_proc->image_types_val & image_type));
  2644.     }
  2645. }
  2646.